package cn.cms.service;

import cn.cms.model.*;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Date;


@Component
@Lazy(false)
public class ScheduleJobManager {

    private static Logger logger = LoggerFactory.getLogger(ScheduleJobManager.class);

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    @PostConstruct
    public void laodScheduleJobs() {

        logger.info("Scheduler : load schedule jobs from databases.........");

        logger.info("Scheduler:load schedule job from databases succeed");
    }

    /**
     * 判断是否过期,仅针对固定时间类型
     *
     * @param schedulableObj
     * @return 2:不属于固定时间类型 1：固定时间类型：开始时间过期，结束时间未过期  0：固定时间类型：开始时间和结束时间均过期
     */
    private int scheduleIsExpired(Schedulable schedulableObj) {
        // 类型为空，不做处理
        if (schedulableObj.getScheduleType() == null) {
            return 0;
        }
        if (schedulableObj.getScheduleType() == 0) {//固定的
            Date now = new Date();
            //固定的开始时间和结束时间都过期了，置为
            if (schedulableObj.getScheduleBeginTime().before(now) && schedulableObj.getScheduleEndTime().before(now)) {
                return 0;
            }
            //固定的开始时间过期了，结束时间未过期，仅执行下线
            if (schedulableObj.getScheduleBeginTime().before(now) && schedulableObj.getScheduleEndTime().after(now)) {
                return 1;
            }
        }
        return 2;
    }

    public void addJob(Schedulable schedulableObj, SchedulableManager schedulableManager, Class clazz) {
        int scheduleIsExpiredResult = scheduleIsExpired(schedulableObj);
        if (scheduleIsExpiredResult == 2) {// 开始时间和结束时间均未过期
            ScheduleJob onLineJob = this.getScheduleJob(schedulableObj, ScheduleJob.GO_ONLINE, schedulableManager, clazz);
            ScheduleJob offLineJob = this.getScheduleJob(schedulableObj, ScheduleJob.GO_OFFLINE, schedulableManager, clazz);
            this.addScheduleJob(onLineJob, ScheduleJob.GO_ONLINE);
            this.addScheduleJob(offLineJob, ScheduleJob.GO_OFFLINE);
        } else if (scheduleIsExpiredResult == 1) {// 开始时间过期，结束时间未过期
            ScheduleJob offLineJob = this.getScheduleJob(schedulableObj, ScheduleJob.GO_OFFLINE, schedulableManager, clazz);
            this.addScheduleJob(offLineJob, ScheduleJob.GO_OFFLINE);
        }
    }

    public void deleteJob(Schedulable schedulableObj, SchedulableManager schedulableManager, Class clazz) {
        int scheduleIsExpiredResult = scheduleIsExpired(schedulableObj);
        if (scheduleIsExpiredResult == 2) {// 开始时间和结束时间都未过期
            ScheduleJob onLineJob = this.getScheduleJob(schedulableObj, ScheduleJob.GO_ONLINE, schedulableManager, clazz);
            ScheduleJob offLineJob = this.getScheduleJob(schedulableObj, ScheduleJob.GO_OFFLINE, schedulableManager, clazz);
            this.deleteJob(onLineJob);
            this.deleteJob(offLineJob);
        } else if (scheduleIsExpiredResult == 1) {// 开始时间过期，结束时间未过期
            ScheduleJob offLineJob = this.getScheduleJob(schedulableObj, ScheduleJob.GO_OFFLINE, schedulableManager, clazz);
            this.deleteJob(offLineJob);
        }
    }

    private ScheduleJob getScheduleJob(Schedulable schedulableObj, String type, SchedulableManager schedulableManager, Class jobClass) {
        ScheduleJob job = new ScheduleJob();
        job.setJobId(schedulableObj.getClass().getSimpleName() + ":" + schedulableObj.getSchedulableId() + ":" + type);
        job.setJobName(schedulableObj.getClass().getSimpleName() + "-" + schedulableObj.getSchedulableId() + "-" + type);
        job.setJobGroup(jobClass.getName());
        job.setScheduleType(schedulableObj.getScheduleType());
        job.setScheduleBeginTime(schedulableObj.getScheduleBeginTime());
        job.setScheduleEndTime(schedulableObj.getScheduleEndTime());
        job.setScheduleRepeatType(schedulableObj.getScheduleRepeatType());
        job.setScheduleRepeatBeginTime(schedulableObj.getScheduleRepeatBeginTime());
        job.setScheduleRepeatEndTime(schedulableObj.getScheduleRepeatEndTime());
        job.setScheduleDuration(schedulableObj.getScheduleDuration());
        job.setScheduleRepeatWeekPattern(schedulableObj.getScheduleRepeatWeekPattern());
        job.setScheduleRepeatMonthPattern(schedulableObj.getScheduleRepeatMonthPattern());
        job.setJobClazz(jobClass);
        job.setTaraget(schedulableObj);
        job.setManager(schedulableManager);
        job.refresh();
        return job;
    }

    private void addScheduleJob(ScheduleJob job, String operType) {
        if (job == null) {
            return;
        }
        try {
            job.setOperType(operType);
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            if (null == trigger) {
                Class clazz = job.getJobClazz();
                JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();
                jobDetail.getJobDataMap().put("scheduleJob", job);

                String cronExpression = null;
                if (ScheduleJob.GO_ONLINE.equals(operType)) {
                    cronExpression = job.getGoOnLineCronExpression();
                } else if (ScheduleJob.GO_OFFLINE.equals(operType)) {
                    cronExpression = job.getGoOffLineCronExpression();
                }
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
                trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();
                scheduler.scheduleJob(jobDetail, trigger);
                logger.info("schedule Job-[" + job.getJobName() + "]-[" + cronExpression + "]");
            } else {
                String cronExpression = null;
                if (ScheduleJob.GO_ONLINE.equals(operType)) {
                    cronExpression = job.getGoOnLineCronExpression();
                } else if (ScheduleJob.GO_OFFLINE.equals(operType)) {
                    cronExpression = job.getGoOffLineCronExpression();
                }
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
                //按新的cronExpression表达式重新构建trigger
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                        .withSchedule(scheduleBuilder).build();
                //按新的trigger重新设置job执行
                scheduler.rescheduleJob(triggerKey, trigger);
                logger.info("reschedule Job-[" + job.getJobName() + "]-[" + cronExpression + "]");
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
            logger.error("add job failed!");
        }
    }

    private boolean deleteJob(ScheduleJob job) {
        if (job == null) {
            return false;
        }
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
        try {
            scheduler.deleteJob(jobKey);
            logger.info("delete Job-[" + job.getJobName() + "]");
            return true;
        } catch (SchedulerException e) {
        }
        return false;
    }
}
